【X68000(Z)アセンブラ講座 第004回 : CG画面に16色BMP画像の表示】 2024/12/27   こんにちは☆ 今回はWindowsの16色BMP画像をX68000(Z)の画面に表示して 壁紙にする簡単なアセンブリ言語アプリのプログラムの解説をします。 以前の講座で予告したとおりの16色(4ビットカラー)で ピクセルサイズが768x512pxの上下逆の画像のBMP形式のファイルは用意できましたか? 用意したBMP画像のファイル名を'BM_TEST0.bmp'に変更して下さい。 今回はサンプルをビルドして実行ファイルを生成し 実行して上手く画像が表示されればOKと言う事にします。 プログラム中の'*'以降行末までコメントなので 次回更新まで何度も読んで理解を深めておくと良いと思います。 次回以降数回に分けて今回のプログラムの解説をしますので安心して下さい。 と言う事で、今回のアプリのサンプルプログラムを公開します。 破線の内側のプログラムを【 BMP16.s 】というテキストファイルに保存して下さい。 -------------------------------------------------------------------------------- ******************************************************************************** * * アプリ名 : BMP16.x * * 高解像度モード(768x512px)用画像表示アプリ * * Ver1.00 * ******************************************************************************** include A:\XC\INCLUDE\DOSCALL.MAC * OSコール用マクロの読み込み include A:\XC\INCLUDE\IOCSCALL.MAC * IOCS(BIOS)コール用マクロの読み込み * 機種別パレット情報 * Win RGBQUAD { Blue[8bit], Green[8bit], Red[8bit], Alpha[8bit] } 32Bit Color * X68K Color { Green[5bit], Red[5bit], Blue[5bit], Alpha[1bit] } 16Bit Color * システム領域のアドレス cg_palette equ $E82000 * CG用パレットデータ,.の先頭アドレス cg_buffer equ $C00000 * CG-VRAMの先頭アドレス cg_scroll_x equ $E80018 cg_scroll_y equ $E8001a .cpu 68000 * CPUのタイプ .data * '.data'以降のデータはデータセクションに配置される file00: dc.b 'BM_TEST0.bmp',0 * 読み込むBMPのファイル名 msg_return: dc.b 13,10,0 * 改行コード('13'+'10') & 終端コード('0') msg01: dc.b 'BMP File Name : ',0 msg02: dc.b 'File Open Error !',13,10,0 msg03: dc.b 'BMP形式のファイルではありません !',13,10,0 msg04: dc.b '4ビットカラー(16色)の画像ではありません !',13,10,0 msg05: dc.b 'CGのサイズが768×512ピクセルではありません !',13,10,0 .even * '.even'以降の偶数アドレスにデータを置く fh: dc.w 0 * ファイルハンドル(ファイル管理番号) viewW: dc.l 0 viewH: dc.l 0 bmp_header_start: * bmp_header_endまでBMPファイルのヘッダーデータ領域 BMFH: * BITMAPFILEHEADER ビットマップファイルヘッダー 14bytes bmph: dc.b 0, 0 * 'B'と'M'ならばBMP形式画像 dc.l 0 * BMPファイルのサイズ dc.w 0 * 予約領域1 dc.w 0 * 予約領域2 dc.l 0 * ファイルヘッダの先頭からビットマップの先頭までの長さ BMIH: * BITMAPINFOHEADER ビットマップインフォヘッダー 40bytes biSize: dc.l 0 * BITMAPINFOHEADER領域のバイト数 biWidth: dc.w 0, 0 * 横ピクセル数 biHeight: dc.w 0, 0 * 縦ピクセル数 biPlanes: dc.w 0 * 1 biBitCount: dc.w 0 * カラーモード(4/8/16/24/32) biCompression: dc.l 0 * BI_RGB biSizeImage: dc.l 0 * イメージの全バイト数 biXPelsPerMeter:dc.l 0 * 水平解像度 biYPelsPerMeter:dc.l 0 * 垂直解像度 biClrUsed: dc.w 0, 0 * 使用されている色の数 biClrImportant: dc.l 0 * 0 bmp_header_end: * BMPファイルのヘッダー領域の終わり .bss * '.bss'以降データ領域 BMPal ds.b 1024 * パレットデータバッファ(1024byte確保する) bmp: ds.b 786432 * ピクセルデータバッファ(786432byte確保する) .text * '.text'以降のプログラムはテキストセクションに配置される start: * スタートアドレス * ユーザーモードからスーパーバイザーモードに移行する moveq.l #_B_SUPER,d0 * d0にIOCS(BIOS)機能番号を代入 move.l #0,d1 * 数値0をd1に代入 movea.l d1,a1 * d1をa1にコピー trap #15 * IOCSコール実行 * 画面モードの設定 moveq.l #_CRTMOD,d0 * 画面モードの設定 move.w #16,d1 * 画面モードの番号 : 16 = 768x512x4 / 8 = 512x512x8 trap #15 * 画面をクリアして表示をオンにする moveq.l #_G_CLR_ON,d0 * 画面をクリアして表示をオンにする trap #15 * CG画面スクロール座標 movea.l #cg_scroll_x,a0 move.w #0,(a0)+ * X座標 move.w #0,(a0)+ * Y座標 pea msg01 * 文字列データmsg01のアドレスをスタックに積む dc.w _PRINT * 文字列の表示 addq.l #4,sp * spの位置を元に戻す pea file00 * ファイル名の先頭アドレスをスタックに積む dc.w _PRINT addq.l #4,sp pea msg_return * 改行するために改行コードのアドレスをスタックに積む dc.w _PRINT addq.l #4,sp bsr load_bmp * BMPファイル読み込みルーチンの呼び出し bsr set_palette * CGパレット設定ルーチンの呼び出し bsr draw_bmp * 画面描画ルーチンの呼び出し * スーパーバイザーモードからユーザーモードに戻る moveq.l #_B_SUPER,d0 move.l SP,a1 trap #15 * アプリ終了 dc.w _EXIT * OSコール実行 : _EXIT = プログラムの終了 load_bmp: * BMPファイルのデータを読み込むルーチン * File Open (ファイルアクセス開始) move.w #0,-(sp) * アクセスモードを指定。0 = 読み込みモード/1 = 書き込みモード pea file00 * ファイル名が書かれた部分の先頭アドレスをスタックに積む dc.w _OPEN * OSコールの実行 : ファイルのオープン addq.l #6,sp * SPの位置を元に戻す tst.l d0 * もしd0がプラスなら、 bpl load_bmp_0 * ラベルload_bmp_0に飛ぶ。 pea msg02 * エラーメッセージ dc.w _PRINT addq.l #4,sp dc.w _EXIT * OSコール実行 : エラーによりソフトウェアの終了 load_bmp_0: move.w d0,fh * _OPENを実行した後にd0にファイルハンドルが入っているので * d0のファイル管理番号をfhに保存する * BMFH read move.l #14,-(sp) * 読み込みバイト数をスタックに積む pea BMFH * アドレスBMFHをスタックに積む move.w fh,-(sp) * ファイルハンドルをスタックに積む dc.w _READ * OSコール実行 : ファイルの読み込み add.l #10,sp * SPの位置を元に戻す cmp.w #'BM',bmph * BMPファイルヘッダーが'BM'か比較する beq load_bmp_1 * 'BM'ならload_bmp_1に飛ぶ pea msg03 dc.w _PRINT addq.l #4,sp dc.w _EXIT * 'BM'以外なのでアプリ終了 load_bmp_1: * BMIH read move.l #40,-(sp) * 40byteのファイルヘッダー pea BMIH move.w fh,-(sp) dc.w _READ * ファイル読み込みのOSコールを実行する lea 10(sp),sp move.w biBitCount,d0 ror.w #8,d0 * d0.wの中身の2進数データを右に8ビットローテート(Intel対策) cmp.w #4,d0 * 4Bitカラー(16色)か比較する beq load_bmp_2 pea msg04 dc.w _PRINT addq.l #4,sp dc.w _EXIT load_bmp_2: * 画像の横ピクセル数チェック move.w biWidth,d0 ror.w #8,d0 * Intel対策(2バイトのデータが左右逆なのでローテートで元に戻す) ext.l d0 * 16ビットのデータをプラスマイナス保ったまま32ビットに拡張する cmp.w #768,d0 * d0.wと数値768を比較する beq load_bmp_3 * branch equal / 比較結果が同じならアドレス'load_bmp_3'に飛ぶ pea msg05 dc.w _PRINT addq.l #4,sp dc.w _EXIT load_bmp_3: * 画像の縦ピクセル数チェック move.w biHeight,d1 ror.w #8,d1 * Intel対策のためバイトデータ逆転させる ext.l d1 cmp.w #512,d1 beq load_bmp_4 pea msg05 dc.w _PRINT addq.l #4,sp dc.w _EXIT load_bmp_4: move.l d1,viewH * BMP パレットの読み込み move.l #64,-(sp) * パレットデータのサイズをスタックに積む pea BMPal move.w fh,-(sp) dc.w _READ lea 10(sp),sp * BMP ピクセルデータの読み込み move.l #768*512/2,-(sp) * ピクセルデータの全てのサイズをスタックに積む pea bmp * bmp = 読み込んだピクセルデータを配置するアドレス move.w fh,-(sp) dc.w _READ lea 10(sp),sp * File Close (ファイルアクセス終了) move.w fh,-(sp) dc.w _CLOSE addq.l #2,sp rts * サブルーチンの最後に配置(呼出元にリターンし次の命令から実行再開) * RGBQUAD { Blue8, Green8, Red8, Reserved8 } = %RRRRRRRR_GGGGGGGG_BBBBBBBB_AAAAAAAA * palette { Green5, Red5, Blue5, Alpha1 } = %GGGGGRRR_RRBBBBB0 * Set CG Palette set_palette: movea.l #cg_palette,a0 * CG用パレットレジスターアドレス lea BMPal,a1 * パレットデータバッファアドレス moveq.l #0,d7 set_palette_1: move.b (a1),d0 * a1が示すアドレスからd0にバイトデータを読み込む * ↓32ビット色の青色成分8ビットのうち上位5ビットを抜き取り16ビット色の青色成分として使う lsr.w #2,d0 * 青色ビットデータの位置を16ビット色内の青色位置に右シフト and.w #%0000000000111110,d0 * 16ビット色の青色成分以外のビットを0にする move.b 1(a1),d1 * a1に1を足したアドレスからd1にバイトデータを読み込む * ↓32ビット色の緑色成分8ビットのうち上位5ビットを抜き取り16ビット色の緑色成分として使う lsl.w #8,d1 * 緑色ビットデータの位置を16ビット色内の緑色位置に左シフト and.w #%1111100000000000,d1 * 16ビット色の緑色成分以外のビットを0にする move.b 2(a1),d2 * a1に2を足したアドレスからd2にバイトデータを読み込む * ↓32ビット色の赤色成分8ビットのうち上位5ビットを抜き取り16ビット色の赤色成分として使う lsl.w #3,d2 * 赤色ビットデータの位置を16ビット色内の赤色位置に左シフト and.w #%0000011111000000,d2 * 16ビット色の赤色成分以外のビットを0にする * 青赤緑のそれぞれの色成分を論理和命令で一つの16ビットカラーに完成させるe or.w d1,d0 or.w d2,d0 or.w #1,d0 * 完成した色データをパレットメモリーに書き込んで更新する move.w d0,(a0)+ * 16ビットデータを書き込んだ後にa0に2を足す * 次の変換元色データを取り出す前にa1のアドレスを更新しておく addq.l #4,a1 addq.w #1,d7 * 色番号を次の番号に更新する cmp.w #16,d7 * 現在の色番号と16を比較する blt set_palette_1 * 現在の色番号が16未満ならアドレスset_palette_1に飛ぶ rts * 呼出元に戻る * Draw BMP draw_bmp: move.l #$C00000,a6 * CG-Vram先頭アドレスをa6に代入する lea bmp,a1 * a1にピクセルデータの先頭アドレスを代入する moveq.l #0,d7 * d7にY座標を代入する draw_bmp_1: moveq.l #0,d6 * d6にX座標を代入する movea.l a6,a5 * a6のアドレス値をa5にコピーする draw_bmp_2: move.b (a1),d0 * アドレスa1の[4bit+4bit=1byte]のピクセルデータd0に読み込む addq.l #1,a1 * a1に1を足して次のアドレスに更新する move.w d0,d1 * d0の値をd1にコピーする lsr.b #4,d0 * d0内の左の4ビットピクセルデータを使うために右に4bitシフト and.b #%0000_1111,d0 * d0のデータの右4bitの状態を保持したまま他ビットを0にする move.w d0,(a5) * アドレスa5内のVRAMアドレスにd0のピクセルを置く addq.l #2,a5 * a5のアドレスに2を足して次のVRAMアドレスに更新する and.b #%0000_1111,d0 * d1のデータの右4bitの状態を保持したまま他ビットを0にする move.w d1,(a5) * アドレスa5内のVRAMアドレスにd1のピクセルを置く addq.l #2,a5 * a5のアドレスに2を足して次のVRAMアドレスに更新する addq.w #2,d6 * X座標d6に2を足す cmp.w #768,d6 * d6の値と768を比較する blt draw_bmp_2 * 比較結果が768未満ならdraw_bmp_2に飛ぶ add.l #1024*2,a6 * 次のY座標を処理するためにa6に横VRAMサイズ2048を足す addq.w #1,d7 * Y座標d7に1を足す cmp.w #512,d7 * d7と512を比較する blt draw_bmp_1 * d7が512未満ならdraw_bmp_1に飛ぶ rts * 呼出元に戻る -------------------------------------------------------------------------------- 【 ビルドの方法 】 Human68Kのコマンドプロンプトから、 A>AS BMP16.s  ↑のように入力して[Enter]キーを押すとエラーが無ければ [ BMP16.o ]と言うオブジェクトファイルが生成されるので A>LK BMP16.o  ↑のように入力して[Enter]キーを押すと [ BMP16.x ]と言う実効ファイルが生成されます。 以上でビルド終了です。 BMP16.xと同じフォルダーに用意した画像ファイル[ BM_TEST0.bmp ]を配置して BMP16.xを実行すると画像が表示されます。 今回の学習は以上です。 次回以降数回゜に分けて今回のソースコードの解説しますので 今回のソースコードの中の気になる点を検索したりして理解しておくと 以後気楽に学習できると思います。 お疲れ様でした(^^)